﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;

namespace Logger
{
    /// <remarks>
    /// Note:
    /// The SMTP server usint implicit ssl(port 465) which Google Gmail is using currently is not supported 
    /// by Microsoft System.Net.Mail.SmtpClient.
    ///
    /// From MSDN:
    /// The SmtpClient class only supports the SMTP Service Extension for Secure SMTP over Transport Layer 
    /// Security as defined in RFC 3207. In this mode, the SMTP session begins on an unencrypted channel, 
    /// then a STARTTLS command is issued by the client to the server to switch to secure communication 
    /// using SSL. See RFC 3207 published by the Internet Engineering Task Force (IETF) for more information. 
    /// An alternate connection method is where an SSL session is established up front before any protocol 
    /// commands are sent. This connection method is sometimes called SMTP/SSL, SMTP over SSL, or SMTPS 
    /// and by default uses port 465. This alternate connection method using SSL is not currently supported. 
    ///
    /// Solutions:
    /// Three types of SMTP server:
    /// - Port 25  (unencrypted) ... System.Net.Mail.SmtpClient works
    /// - Port 587 (TLS) ... System.Net.Mail.SmtpClient works with SmtpClient.EnableSsl property = true
    /// - Port 465 (SSL) ... CDO (Collaborative Data Objects) directly through COM 
    ///                      (Add Reference of COM "Microsoft CDO For Windows 2000 Library")
    ///                       http://support.microsoft.com/kb/310212)
    /// Thread safty: This class is thread safe. 
    /// </remarks>
    /// <dates>
    /// 15/05/2011 - created by Chiaki Sato
    /// </dates>
    public class MailLogger : ILogger
    {
        private ILoggerMessage stackTraceMessage;
        private ILoggerMessage hostAddressMessage;
        private string emailAddressFrom;
        private string emailAddressTo;
        private string username;
        private string password;
        private string messageSubject;
        private string smtpServer;
        private string smtpUseSsl;
        private int portNumber;

        // Log type
        public enum LogType { UNIT = 1, COMPO = 2 }

        // Executable type
        public enum ExeType { SERVER, CLIENT }
        private ExeType exeType;

        // CDO configulation index
        private const string CDO_SMTP_SERVER = "http://schemas.microsoft.com/cdo/configuration/smtpserver";
        private const string CDO_SMTP_SERVER_PORT = "http://schemas.microsoft.com/cdo/configuration/smtpserverport";
        private const string CDO_SEND_USING = "http://schemas.microsoft.com/cdo/configuration/sendusing";
        private const string CDO_SMTP_AUTHENTICATE = "http://schemas.microsoft.com/cdo/configuration/smtpauthenticate";
        private const string CDO_SEND_USER_NAME = "http://schemas.microsoft.com/cdo/configuration/sendusername";
        private const string CDO_SEND_PASSWORD = "http://schemas.microsoft.com/cdo/configuration/sendpassword";
        private const string CDO_SMTP_USE_SSL = "http://schemas.microsoft.com/cdo/configuration/smtpusessl";

        // Defult e-mail message parameters for Google gmail SMTP server 
        private const string DEFAULT_EMAIL_ADDRESS_FROM = "frankensteam.2013@gmail.com";
        private const string DEFAULT_EMAIL_ADDRESS_TO = "frankensteam.2013@gmail.com";
        private const string DEFAULT_USER_NAME = "frankensteam.2013@gmail.com";
        private const string DEFAULT_PASSWORD = "frankensteam";
        private const string DEFAULT_SMTP_SERVER = "Smtp.gmail.com";
        private const int DEFAULT_PORT_NUMBER = 465;
        private const string DEFAULT_MESSAGE_SUBJECT_SERVER = "Frankensteam Server - System Alert Mail";
        private const string DEFAULT_MESSAGE_SUBJECT_CLIENT = "Frankensteam Client - System Alert Mail";
        private const string DEFAULT_SMTP_USE_SSL = "true";

        private const string MESSAGE_HEADER_SERVER = "Frankensteam Project - Server Log\n\n" +
                                              "The following log message was reported on Frankensteam server during of its runtime.\n" +
                                              "(This e-mail was sent by Mail Logger, e-mail log notification service.)\n\n" +
                                              "--- Start Log ---------------------------------\n\n";
        private const string MESSAGE_HEADER_CLIENT = "Frankensteam Project - Client Log\n\n" +
                                              "The following log message was reported on Frankensteam client during of its runtime.\n" +
                                              "(This e-mail was sent by Mail Logger, e-mail log notification service.)\n\n" +
                                              "--- Start Log ---------------------------------\n\n";
        private const string MESSAGE_FOOTER = "\n\n--- End Log -----------------------------------";

        private object lockObject;

        /// <summary>
        /// Default constructor
        /// </summary>
        public MailLogger(LogType logType, ExeType exeType)
        {
            lockObject = new object();

            this.exeType = exeType;

            stackTraceMessage = new StackTraceMessage((StackTraceMessage.LogType)logType);
            hostAddressMessage = HostAddressSingleton.Instance;

            this.emailAddressFrom = DEFAULT_EMAIL_ADDRESS_FROM;
            this.emailAddressTo = DEFAULT_EMAIL_ADDRESS_TO;
            this.username = DEFAULT_USER_NAME;
            this.password = DEFAULT_PASSWORD;
            this.smtpServer = DEFAULT_SMTP_SERVER;
            this.portNumber = DEFAULT_PORT_NUMBER;
            this.smtpUseSsl = DEFAULT_SMTP_USE_SSL;
            if (exeType == ExeType.CLIENT)
            {
                this.messageSubject = DEFAULT_MESSAGE_SUBJECT_CLIENT;
            }
            else
            {
                this.messageSubject = DEFAULT_MESSAGE_SUBJECT_SERVER;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public MailLogger(string emailAddressFrom, string emailAddressTo, string username, string password,
            string smtpServer, int portNumber, string smtpUseSsl, string messageSubject)
        {
            lockObject = new object();

            this.emailAddressFrom = emailAddressFrom;
            this.emailAddressTo = emailAddressTo;
            this.username = username;
            this.password = password;
            this.smtpServer = smtpServer;
            this.portNumber = portNumber;
            this.smtpUseSsl = smtpUseSsl;
            this.messageSubject = messageSubject;
        }

        /// <summary>
        /// Write a log as e-mail message
        /// </summary>
        /// <param name="message">User log message</param>
        public string Write(string message)
        {
            // For tread safty
            lock (lockObject)
            {
                string logMessage = hostAddressMessage.CreateLogMessage()
                                  + stackTraceMessage.CreateLogMessage()
                                  + message;

                if (exeType == ExeType.SERVER)
                {
                    logMessage = MESSAGE_HEADER_SERVER + logMessage + MESSAGE_FOOTER;
                }
                else
                {
                    logMessage = MESSAGE_HEADER_CLIENT + logMessage + MESSAGE_FOOTER;
                }

                SendMailCdo(logMessage);

                return (logMessage);
            }
        }

        /// <summary> 
        /// Send an e-mail using CDO. 
        /// <param name="messageBody">A log message</param>
        /// </summary> 
        /// <remarks>
        /// Test Google Gmail account to which the log mails are sent is:
        /// user id: "Frankensteam@gmail.com"
        /// password: "jup9ANez"
        /// </remarks>
        private void SendMailCdo(string messageBody)
        {
            try
            {
                // Note! add a Reference of COM "Microsoft CDO For Windows 2000 Library"
                CDO.Message message = new CDO.Message();
                CDO.IConfiguration configuration = message.Configuration;
                ADODB.Fields fields = configuration.Fields;

                // Configuring CDO settings

                ADODB.Field field = fields[CDO_SMTP_SERVER];
                field.Value = smtpServer;

                field = fields[CDO_SMTP_SERVER_PORT];
                field.Value = portNumber;

                field = fields[CDO_SEND_USING];
                field.Value = CDO.CdoSendUsing.cdoSendUsingPort;

                field = fields[CDO_SMTP_AUTHENTICATE];
                field.Value = CDO.CdoProtocolsAuthentication.cdoBasic;
                // cdoAnonymous ... Do not authenticate. 
                // cdoBasic ... Basic clear-text authentication. (requires sendusername and sendpassword) 
                // cdoNTLM ... The current process security context is used to authenticate with the service. 

                field = fields[CDO_SEND_USER_NAME];
                field.Value = username;

                field = fields[CDO_SEND_PASSWORD];
                field.Value = password;

                field = fields[CDO_SMTP_USE_SSL];
                field.Value = smtpUseSsl;

                fields.Update();

                message.From = emailAddressFrom;
                message.To = emailAddressTo;
                message.Subject = messageSubject;
                message.TextBody = messageBody;

                message.Send();

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary> 
        /// Send an e-mail by the sample code of C# text book. 
        /// </summary> 
        //public void SendMail(string messageBody)
        //{
        //    MailMessage message;
        //
        //    using (message = new MailMessage())
        //    {
        //        try
        //        {
        //            message.From = new MailAddress(emailAddressFrom);
        //            message.To.Add(emailAddressTo);
        //            message.Subject = messageSubject;
        //            message.Body = messageBody;
        //
        //            SmtpClient client = new SmtpClient(smtpServer, portNumber);
        //
        //            client.Credentials = new System.Net.NetworkCredential(username, password);
        //
        //            client.Send(message);
        //        }
        //        catch (Exception ex)
        //        {
        //            Console.WriteLine("Exception: " + ex.Message);
        //        }
        //    }
        //}

    }

}
